package com.hp.bon.sgw.core;

import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;

import org.dom4j.Document;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.hp.bon.sgw.Constants;
import com.hp.bon.sgw.bean.AuthResult;
import com.hp.bon.sgw.bean.CongestionBean;
import com.hp.bon.sgw.bean.XmlMessage;
import com.hp.bon.sgw.convert.DataExchangerImpl;
import com.hp.bon.sgw.core.processor.ForwardProcessor;
import com.hp.bon.sgw.core.processor.Processor;
import com.hp.bon.sgw.core.route.RouteInfo;
import com.hp.bon.sgw.domain.Node;
import com.hp.bon.sgw.service.AuthControlService;
import com.hp.bon.sgw.service.BlackListSgwViewService;
import com.hp.bon.sgw.service.ConfigService;
import com.hp.bon.sgw.service.FluxControlService;
import com.hp.bon.sgw.service.FluxControlSgwViewService;
import com.hp.bon.sgw.service.QueryHomeAreaByAccNbrService;
import com.hp.bon.sgw.service.RSASignService;
import com.hp.bon.sgw.service.ReplaceDomainByAccNbrService;
import com.hp.bon.sgw.service.SGWCapabilityReqXMLValidator;
import com.hp.bon.sgw.start.SpringContext;
import com.hp.bon.sgw.threads.BlacklistMonitorThread;
import com.hp.bon.sgw.threads.CapabilityLifeManager;
import com.hp.bon.sgw.util.Comutils;
import com.hp.bon.sgw.util.dcc.DccAvpHelper;
import com.hp.bon.sgw.wkflow.BONFlowEngine;
import com.hp.ocs.diameter.message.Message;

public class BuzProcessorManager {
	public static final Logger logger = LoggerFactory.getLogger(BuzProcessorManager.class);
	private static BuzProcessorManager instance;

	public static BuzProcessorManager getInstance() {
		if (instance == null) {
			instance = SpringContext.getApplicationContext().getBean(BuzProcessorManager.class);
		}
		return instance;
	}

	public ConcurrentHashMap<String, CallParam> processQueue = new ConcurrentHashMap<String, CallParam>();
	public HashMap<String, Processor> processorMap = new HashMap<String, Processor>();

	public void init() {
		logger.info("init route map...");

	}

	private BONFlowEngine bonFlowEngine;

	public void processRequest(CallParam callParam) {
		
		if(ConfigService.tpHcodes99Map!=null && ConfigService.tpHcodes99Map.size()>0)
		{
			if(callParam.getServiceContextId()!=null && callParam.getServiceContextId().indexOf("@")>-1)
			{
				String capability = DccAvpHelper.getCapability(callParam.getServiceContextId());
				if(capability.equalsIgnoreCase("Recharge.VC"))
				{
					ReplaceDomainByAccNbrService replaceDomainByAccNbrService = ReplaceDomainByAccNbrService.getInstance();
					AuthResult result = replaceDomainByAccNbrService.replaceDomain(callParam);
					if(result.getCode() != Constants.ResultCode.SUCCESS)
					{
						callParam.setRetCode(result.getCode());
						callParam.setResultMsg(result.getHint());
						callParam.getAsynHandler().onCallResult(callParam);
						return;
					}
				}
			}
		}
		
				
		
		if (callParam.getServiceContextId().contains("@000")) {// 做归属地查询
			QueryHomeAreaByAccNbrService queryHomeAreaByAccNbrService = SpringContext.getApplicationContext().getBean(QueryHomeAreaByAccNbrService.class);
			int result = queryHomeAreaByAccNbrService.queryHomeArea(callParam);
			if (result != Constants.ResultCode.SUCCESS) {
				callParam.setRetCode(result);
				callParam.setResultMsg("Query Home Area failed!");
				callParam.getAsynHandler().onCallResult(callParam);
				return;
			}
		}

		if (bonFlowEngine.hashCapabilityWorkflow(callParam.getCapability())) {
			bonFlowEngine.startFlow(callParam);
			return;
		}

		Processor processor =  processorMap.get(callParam.getCapability());
		Processor newProcessor;
		if (processor == null) {
			newProcessor = new ForwardProcessor();
		} else {// 复制一个新的processor，某些processor中需要保存状态数据，例如流程定制的processor需要记录执行的步骤
			newProcessor = SpringContext.getApplicationContext().getBean(processor.getClass());
		}

		callParam.setProcessor(newProcessor);
		newProcessor.process(callParam);
	}

	public int checkCallParamForDcc(CallParam callParam, StringBuilder errMsg) {

		Node node = SpringContext.getInstance().getConfigService().getNodeByName(callParam.getUserName());
		if (node == null) {
			errMsg.append("AuthFailed: OriginalHost not found");
			return Constants.DIAMETER_RESULT_UNABLE_TO_COMPLY;
		}
		callParam.setFromNode(node);

		AuthResult autResult = AuthControlService.getInstance().authAccess(callParam, null, true, false);
		if (autResult.getCode() != AuthResult.SUCCESS) {
			errMsg.append(autResult.getHint());
			return autResult.getCode();
		}

		// 检查Pertri网状态，是否处于停工状态
		if (!SpringContext.getInstance().getPertriBean().canHandleNewRequest()) {
			errMsg.append(Constants.ResultCode.SYSTEM_BUSY + "," + callParam.getSessionId() + ",ignore any new request , for system is in status of " + SpringContext.getInstance().getPertriBean().getPertiStatusName());
			return Constants.DIAMETER_RESULT_UNABLE_TO_COMPLY;
		}
		String capability = callParam.getCapability();
		int priority = this.calcMsgPrority(callParam);
		// CPU 超负荷处理
		if (SpringContext.getInstance().getCPUOverloadBean().dropRequest(priority, callParam.getSessionId(), capability)) {
			errMsg.append(Constants.ResultCode.SYSTEM_BUSY + "," + callParam.getSessionId() + ",cpu overload");
			return Constants.DIAMETER_RESULT_UNABLE_TO_COMPLY;
		}
		// 拥塞控制处理
		if (SpringContext.getInstance().getCongestionBean().dropRequest(priority, callParam.getSessionId(), capability)) {
			errMsg.append(Constants.ResultCode.SYSTEM_BUSY + "," + callParam.getSessionId() + ",system congestioned");
			return Constants.DIAMETER_RESULT_UNABLE_TO_COMPLY;
		}

		// 生命周期控制 add by yudan
		if (!CapabilityLifeManager.getInstance().checkActive(callParam.getFromNode().getHostId(), callParam.getServiceContextId())) {
			callParam.setRetCode(Constants.ResultCode.Capbility_OutOf_Life_Time);
			errMsg.append("Capbility outof life time");
			return Constants.ResultCode.Capbility_OutOf_Life_Time;
		}

		// 检查流量是否超标
		int fluxRetCode = FluxControlService.getInstance().checkUserFlux(callParam, ((Message) callParam.getRequest()).header().messageLength());
		if (fluxRetCode == Constants.ResultCode.FLUX_DISCONNECT) {
			errMsg.append(fluxRetCode + "," + callParam.getSessionId() + ",close peer for flux control," + callParam.getUserName());
			return Constants.DIAMETER_RESULT_RESOURCES_EXCEEDED;
		} else if (fluxRetCode == Constants.ResultCode.MESSAGE_SIZE_OVER) {// 单个包大小超标
			errMsg.append(fluxRetCode + "," + callParam.getSessionId() + ",dcc msg size to large");
			return Constants.DIAMETER_RESULT_UNABLE_TO_COMPLY;
		}

		// 检查流量控制:能力视图 add by wanghan
		int fluxRetCodeNew = FluxControlSgwViewService.getInstance().checkUserFlux(callParam);
		if (fluxRetCodeNew == Constants.ResultCode.FLUX_DISCONNECT) {
			errMsg.append("limitted, not allowed anymore , reached traffic  limit ,please wait some minutes ");
			return Constants.ResultCode.FLUX_DISCONNECT;
		}

//		// 预处理服务链，包括内容过滤、根据关键字流量控制等
//		ProcessResult processResult = ServiceChain.getInstance().doPreServices(callParam);
//		if (processResult != null && processResult.getRet() != ResultCode.SUCCESS) {
//			errMsg.append(processResult.getErrMsg());
//			return processResult.getRet();
//		}

//		// 按能力进行特定错码限流,检查是否流量超标
//		int fluxScIdRetCode = FluxControlByRcService.getInstance().checkRcFlux(callParam, ((Message) callParam.getRequest()).header().messageLength());
//		if (fluxScIdRetCode == Constants.ResultCode.FLUX_DISCONNECT) {
//			errMsg.append("not allowed anymore , reached capability result code traffic limit ,please wait some minutes ");
//			return Constants.ResultCode.FLUX_DISCONNECT;
//		}

		String result = FluxControlService.getInstance().filterContent(callParam.getServiceContextId(), 1, (Message) callParam.getRequest());
		if (result != null) {
			errMsg.append("11102," + callParam.getSessionId() + ",Content Filter Failed ,Bad value of:" + result);
			return Constants.DIAMETER_RESULT_INVALID_AVP_VALUE;
		}
		result = BlacklistMonitorThread.getInstance().checkBlackList(callParam);
		if (result != null) {
			errMsg.append("usr in blacklist:" + result);
			return Constants.DIAMETER_RESULT_INVALID_AVP_VALUE;
		}

		// 黑名单检查：能力视图 add by wanghan
		result = BlackListSgwViewService.getInstance().checkAllowConnect(callParam);
		if(result != null){
			errMsg.append("usr in sgw-view blacklist:" + result);
			return Constants.ResultCode.Usr_in_Blacklist;
		}
		return Constants.ResultCode.SUCCESS;

	}

	/**
	 * 检查调用参数是否有效，是否可以通过验证
	 * 
	 * @param bonParam
	 * @return
	 */
	public int checkBonCallParamForSoap(CallParam callParam, StringBuilder errMsg) {
		XmlMessage request = (XmlMessage) callParam.getRequest();
		String uid = callParam.getUserName();
		if ((uid == null) || uid.isEmpty() || uid.length() > 64) {
			errMsg.append("UID is illegal");
			return Constants.ResultCode.VALUE_ERROR;
		}
		String rawData = request.getDATA();
		if (rawData == null) {
			errMsg.append("Missing \"DATA\" Tag.");
			return Constants.ResultCode.MISSING_TAG;
		}

		Document doc;
		try {
			doc = Comutils.parseText(rawData);
			request.setDataElement(doc.getRootElement());
		} catch (Exception e) {
			errMsg.append("Invalid request DATA format ,not valid xml " + e.toString());
			return Constants.ResultCode.UNKNOW_TAG;
		}

		int reqByts = 0;
		byte[] reqDataBytes = null;
		try {
			reqDataBytes = rawData.getBytes("ISO-8859-1");
		} catch (UnsupportedEncodingException e2) {
			logger.error("encoding failed " + e2);
		}
		// 检查Pertri网状态，是否处于停工状态
		if (!SpringContext.getInstance().getPertriBean().canHandleNewRequest()) {
			String inf = "ignore any new request , for system is in status of " + SpringContext.getInstance().getPertriBean().getPertiStatusName();
			logger.info(inf);
			errMsg.append(inf);
			return Constants.ResultCode.SYSTEM_BUSY;
		}
		String scid = callParam.getServiceContextId();
		if (scid == null || scid.equals("")) {
			errMsg.append("scid is null");
			return Constants.ResultCode.UNKNOW_TAG;
		} else if (!scid.contains("@")) {
			errMsg.append("scid is illegal");
			return Constants.ResultCode.UNKNOW_TAG;
		}
		Node node = SpringContext.getInstance().getConfigService().getNodeByName(callParam.getUserName());
		String capability = DccAvpHelper.getCapability(scid);

		if (node == null) {
			errMsg.append("AuthFailed: UserName not found");
			return 11001;
		}
		callParam.setFromNode(node);

		AuthResult autResult = AuthControlService.getInstance().authWSAccess(callParam, reqDataBytes, true);
		if (autResult.getCode() != AuthResult.SUCCESS) {
			errMsg.append(autResult.getHint());
			return autResult.getCode();
		}
		int priority = calcMsgPrority(callParam);

		if (SpringContext.getInstance().getCPUOverloadBean().dropRequest(priority, callParam.getSessionId(), capability)) {
			errMsg.append("cpu overload, wait a moment please.");
			return Constants.ResultCode.SYSTEM_BUSY;
		}
		// 拥塞控制处理
		if (CongestionBean.getInstance().dropRequest(priority, callParam.getSessionId(), capability)) {
			errMsg.append("system congestioned ,please dont't send request anymore ");
			return Constants.ResultCode.SYSTEM_BUSY;
		}
		
		//生命周期控制 add by wanghan
		if(!CapabilityLifeManager.getInstance().checkActive(callParam.getFromNode().getHostId(), callParam.getServiceContextId())){
			callParam.setRetCode(Constants.ResultCode.Capbility_OutOf_Life_Time);
			errMsg.append("Capbility outof life time");
			return Constants.ResultCode.Capbility_OutOf_Life_Time;
		}
				
		// 检查是否流量超标
		int fluxRetCode = FluxControlService.getInstance().checkUserFlux(callParam, reqByts);
		if (fluxRetCode == Constants.ResultCode.FLUX_DISCONNECT) {
			errMsg.append("not allowed anymore , reached traffic  limit ,please wait some minutes ");
			return Constants.ResultCode.FLUX_DISCONNECT;
		} else if (fluxRetCode == Constants.ResultCode.MESSAGE_SIZE_OVER) {// 单个包大小超标
			errMsg.append("message size too large ");
			return Constants.ResultCode.MESSAGE_SIZE_OVER;
		}
		
		// 检查流量控制:能力视图 add by wanghan
		int fluxRetCodeNew = FluxControlSgwViewService.getInstance().checkUserFlux(callParam);
		if (fluxRetCodeNew == Constants.ResultCode.FLUX_DISCONNECT) {
			errMsg.append("limitted, not allowed anymore , reached traffic  limit ,please wait some minutes ");
			return Constants.ResultCode.FLUX_DISCONNECT;
		}
		
//		// 预处理服务链，包括内容过滤、根据关键字流量控制等
//		ProcessResult processResult = ServiceChain.getInstance().doPreServices(callParam);
//		if(processResult != null && processResult.getRet() != ResultCode.SUCCESS)
//		{
//			errMsg.append(processResult.getErrMsg());
//			return processResult.getRet();
//		}
//		//按能力进行特定错码限流,检查是否流量超标
//		int fluxScIdRetCode = FluxControlByRcService.getInstance().checkRcFlux(callParam, reqByts);
//		if(fluxScIdRetCode == Constants.ResultCode.FLUX_CAPA_RESULT)
//		{
//			errMsg.append("not allowed anymore , reached capability result code traffic limit ,please wait some minutes ");
//			return Constants.ResultCode.FLUX_CAPA_RESULT;
//		}
		
		String result = FluxControlService.getInstance().filterContent(callParam.getServiceContextId(), callParam.getAsynHandler().getProtocolType(), callParam.getRequest());
		if (result != null) {
			errMsg.append("Content Filter Failed ,Bad value of:" + result);
			return 11102;
		}
		result = BlacklistMonitorThread.getInstance().checkBlackList(callParam);
		if (result != null) {
			errMsg.append("usr in blacklist:" + result);
			return Constants.ResultCode.Usr_in_Blacklist;
		}
		
		// Request的XML 数据校验
		StringBuilder xmlValidatHint = new StringBuilder();
		int errCode = SGWCapabilityReqXMLValidator.getInstance().validateRequest(capability, request.getDataElement(), xmlValidatHint);
		if (errCode != 0) {
			errMsg.append("Validate Request Failed:" + xmlValidatHint.toString());
			return errCode;
		}
		
		//黑名单检查：能力视图  add by wanghan
		result = BlackListSgwViewService.getInstance().checkAllowConnect(callParam);
		if(result != null){
			errMsg.append("usr in sgw-view blacklist:" + result);
			return Constants.ResultCode.Usr_in_Blacklist;
		}
				
		return Constants.ResultCode.SUCCESS;
	}

	/**
	 * 取优先级
	 * 
	 * @param serviceCtxID
	 *            能力
	 * @param neHostID
	 *            网元
	 * @param userName
	 *            用户
	 * @return
	 */
	public int calcMsgPrority(CallParam callParam) {
		int priority = Constants.PRIORITY_DEFAULT;
		if (callParam.getCapabilityBean() == null) {
			if (logger.isDebugEnabled()) {
				logger.debug("can't find priority define for: " + callParam.getUserName() + "," + callParam.getCapability());
			}
		} else {
			priority = callParam.getCapabilityBean().getPriority();
		}
		if (logger.isDebugEnabled()) {
			logger.debug("message priority For NE " + callParam.getUserName() + " capability:" + callParam.getCapability() + " is " + priority);
		}
		return priority;
	}

	public XmlMessage convertMessageToAck(CallParam callParam) {
		XmlMessage ack = new XmlMessage(false);
		ack.setMID(callParam.getSessionId());
		ack.setRTT(new SimpleDateFormat("yyyyMMddHHmmssSSS").format(System.currentTimeMillis()));
		DataExchangerImpl.getInstance().convertCCAToXML(callParam.getServiceContextId(), (Message) callParam.getRecvResponse(), callParam);

		ack.setRC(Integer.toString(callParam.getRetCode()));
		ack.setDS(callParam.getResultMsg());
		Element resultData = null;
		try {
			resultData = (Element) callParam.getResponse();
		} catch (Exception e) {
			//
		}
		String resultTxtData = null;
		if (resultData != null) {
			// 如果请求的XML不是SGW_XML_ROOT_NAME，可能需要转换内容，
			XmlMessage request = (XmlMessage) callParam.getRequest();
			boolean needRemveRootEl = !(request.getDataElement().getName().equals(Constants.SGW_XML_ROOT_NAME) && resultData.getName().equals(Constants.SGW_XML_ROOT_NAME));
			if (needRemveRootEl) {
				Element srvInfEl = resultData.element("Service_Information");
				if (srvInfEl == null && resultData.elements().size() > 1) {
					// 取第一个
					srvInfEl = ((Element) resultData.elementIterator().next());
				}
				if (srvInfEl != null) {
					resultTxtData = srvInfEl.asXML();
				}
			} else {
				resultTxtData = resultData.asXML();
			}
			ack.setDATA(resultTxtData);
			ack.setSIG(RSASignService.getInstance().sinDATA(resultTxtData, callParam.getUserName(), callParam.getSignType()));
		}
		return ack;
	}

	public static int sendCallParam(CallParam newCallParam) {
		RouteInfo r = ChannelServer.findRoute(newCallParam.getServiceContextId());
		int ret = 0;
		if (r == null) {
			ret = 3002;// 没有查到路由
		} else if (r.result != 0) {
			ret = r.result;
		}
		if (ret != 0) {
			newCallParam.setRetCode(3002);
			newCallParam.setResultMsg("no Route");
			return 3002;
		}
		newCallParam.setToNode(r.curNode);
		InprogressMessageQueue.getInstance().addRequest(newCallParam);
		r.curNode.getChannel().sendRequest(newCallParam);
		return Constants.ResultCode.SUCCESS;
	}

	public void setProcessorMap(HashMap<String, Processor> processorMap) {
		this.processorMap = processorMap;
	}

	@Autowired
	public void setBonFlowEngine(BONFlowEngine bonFlowEngine) {
		this.bonFlowEngine = bonFlowEngine;
	}

}